package dnbW.entityW;

import dnbW.dbW.DataSource;
import java.util.Arrays;

/**
 * Encapsulates an XML path from the root node to an element node.
 * It consists of an ordered set of elements along the path
 * @author truongbaquan
 */
public class Path implements IPath {
    // <editor-fold defaultstate="collapsed" desc="Data Source">
    private DataSource dataSource;

    @Override
    public DataSource getDataSource() {
        return dataSource;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Elements">
    private String[] elements;

    @Override
    public String[] getElements() {
        return this.elements;
    }

    @Override
    public String getElementAtLevel(int level) {
        return this.elements[level-1];
    }
    // </editor-fold>

    protected Path(DataSource dataSource, String[] elements) {
        this.dataSource = dataSource;
        this.elements = elements;
    }

    @Override
    public boolean isAttribute() {
        return this.elements[this.elements.length-1].startsWith("@");
    }

    @Override
    public int getLevel() {
        return this.elements.length;
    }

    @Override
    public String toSQLPathExp() {
        StringBuilder builder = new StringBuilder();
        for (String element : this.elements) {
            builder.append("." + element + "#");
        }
        return builder.toString();
    }

    @Override
    public String toPathExp() {
        StringBuilder builder = new StringBuilder();
        for (String element : this.elements) {
            builder.append("/" + element);
        }
        return builder.toString();
    }

    @Override
    public String toString() {
        return toPathExp();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Path other = (Path) obj;
        if (!this.toSQLPathExp().equals(other.toSQLPathExp())) return false;
        return true;
    }

    @Override
    public int hashCode() {
        return this.toSQLPathExp().hashCode();
    }

    /**
     * Creates a path from its path expression (e.g. uniprot/entry/gene)
     * @param dataSource        Data Source
     * @param pathExpression    Path Expression
     * @return                  New path with the given path expression
     */
    public static Path buildPathFromPathExp(DataSource dataSource, String pathExpression) {
        if (pathExpression.startsWith("/")) {
            pathExpression = pathExpression.substring(1);
        }
        String[] elements = pathExpression.split("/");
        return new Path(dataSource, elements);
    }

    /**
     * Creates a path from its SQL path expression (e.g. .uniprot#.entry#.gene#)
     * @param dataSource
     * @param sqlPathExpression
     * @return
     */
    public static Path buildPathFromSqlPathExp(DataSource dataSource, String sqlPathExpression) {
        sqlPathExpression = sqlPathExpression.substring(0, sqlPathExpression.length()-1); // omit the last #
        String[] elements = sqlPathExpression.split("#");
        for (int i = 0; i < elements.length; i++) {
            elements[i] = elements[i].substring(1); // omit the first . in each element
        }
        return new Path(dataSource, elements);
    }

    public static int findLCALevel(Path path1, Path path2) {
        int maxLevel = Math.min(path1.getLevel(), path2.getLevel());

        // find the first level that the elements of two paths are different
        int level = 1;
        for (level = 1; level <= maxLevel; level++) {
            if (!path1.getElementAtLevel(level).equals(path2.getElementAtLevel(level))) {
                break;
            }
        }
        return level - 1;
    }
    
    /**
     * Produces an ancestor path of the given path at certain level
     * @param path  The input path
     * @param level The ancestor's level
     * @return      The ancestor path
     */
    public static Path getAncestorPath(IPath path, int level) {
        if (level <= 0 || level > path.getLevel()) return null;
        String[] ancestorElements = new String[level];
        for (int lvl = 1; lvl <= level; lvl++) {
            ancestorElements[lvl-1] = path.getElementAtLevel(lvl);
        }
        return new Path(path.getDataSource(), ancestorElements);
    }
}
